<?php
/**
 * I-Cafe framework
 * 
 * @category CF
 * @package CF
 * @link http://www.i-cafe.org
 * @license http://www.i-cafe.org/license
 * @version 04-04-2010
 * @author tcl_java@yahoo.com
 */
class CF_Observable
{
	/**
	 * 
	 * @var string
	 */
	const VALUE_RETURN = 'CFSUTIOEWFJSIDGFWHEORUISOFDJSGFUEWIOFDUAJSFOJDSFKJSDKLFJASDF';
	
	/**
	 * Event data
	 * @var array
	 */
	protected $_events = array(
		'beforeconstruct'	=> array(),
		'construct'			=> array(),
		'destruct'			=> array()
	);
	
	/**
	 * add events
	 * @param string|array $events
	 * @return {void}
	 */
	public function addEvents($events)
	{
		if(1 < func_num_args()){
			$events = func_get_args();
		}else{
			if(is_string($events)){
				$events = explode(',', preg_replace('/\s+/', '', $events));
			}
		}
		
		foreach($events as $event){
			if(!$this->hasEvent($event))
				$this->_events[$event] = array();
		} 
	}
	
	/**
	 * Check event exist
	 * @param string $event_name
	 * @return boolean
	 */
	public function hasEvent($eventName)
	{
		return isset($this->_events[$eventName]);
	}
	
	/**
	 * add a listener
	 * 
	 * @param string $event_name
	 * @param string|array $callback
	 * @return void
	 */
	public function addListener($eventName, $callback)
	{
		# event has register ?
		if(!$this->hasEvent($eventName))
			throw new CF_Observable_Exception(
				"Event [ <b>$eventName</b> ] does not register."
			);
		
		# check valid callback
		if(false === is_callable($callback, false))
			throw new CF_Observable_Exception("Callback is not valid.");
		
		# add callback to event
		$this->_events[$eventName][] = $callback;
	}
	
	/**
	 * add a listener
	 * 
	 * @param string $event_name
	 * @param string|array $callback
	 * @return void
	 */
	public function on($eventName, $callback)
	{
		$this->addListener($eventName, $callback);
	}
	
	/**
	 * Remove listener
	 * @param string $event_name
	 * @param string|array $callback
	 * @return void
	 */
	public function removeListener($eventName, $callback = null)
	{
		if(!$this->hasEvent($eventName)) return;
		
		if(null === $callback)
			$this->_events[$eventName] = array();
		else{
			$key = array_search($callback, $this->_events[$eventName]);
			if(false === $key) return;
			unset($this->_events[$eventName][$key]);
		}
	}
	
	/**
	 * Remove listener
	 * @param string $event_name
	 * @param string|array $callback
	 * @return void
	 */
	public function un($eventName, $callback = null)
	{
		$this->removeListener($eventName, $callback);
	}
	
	/**
	 * Notify a event
	 * @param string $event_name
	 * @param array $args [optional]
	 * @return boolean false if all callback return false
	 */
	public function fireEvent($eventName, array $args = array())
	{
		if(!$this->hasEvent($eventName))
			throw new CF_Observable_Exception(
				"Event \"$eventName\" does not register."
			);
			
		if(empty($this->_events[$eventName])) return;
		
		$res = true;
		$callbacks = $this->_events[$eventName];
		foreach($callbacks as $callback){
			if(false === call_user_func_array($callback, $args))
				$res = false;
		}
		
		return $res;
	}
	
	/**
	 * @return {void}
	 */
	public function __destruct()
	{
		# notify destruct event
		$this->fireEvent('destruct', array($this));
	}
}